unit DIBImageUtils;

interface

uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Math;

type

  TQuadrilateral = packed record
    RightTop: TPoint;
    RightBottom: TPoint;
    LeftTop: TPoint;
    LeftBottom: TPoint;
  end;

  Quadrilateral = ^TQuadrilateral;

  TLine = packed record
    BeginPoint: TPoint;
    EndPoint: TPoint;
  end;

  Line = ^TLine;

  DIBOperTemplete = (doVertical, doHorizontal, doCross, doRect);

  TOperModel = packed record
    Templete: DIBOperTemplete;
    Size: Integer;
  end;

function CoordinateConvert(SourceWidth, SourceHeight: Integer;
  const Coordinate: TPoint; TaregtWidht, TargetHeight: Integer)
  : TPoint; overload;

function CoordinateConvert(SourceWidth, SourceHeight: Integer;
  const X, Y: Integer; TaregtWidht, TargetHeight: Integer): TPoint; overload;

procedure DiffValue(SourceData, TargetData: PAnsiChar;
  AWidth, AHeight: Integer); overload;

procedure DiffValue(SourceData, TargetData: PAnsiChar; AWidth, AHeight: Integer;
  const Area: TQuadrilateral; Threshold: Integer); overload;

procedure Erode(SourceData, TargetData: PAnsiChar; AWidth, AHeight: Integer;
  Model: TOperModel); overload;

procedure Erode(SourceData, TargetData: PAnsiChar; AWidth, AHeight: Integer;
  Model: TOperModel; const Area: TQuadrilateral); overload;

function DenoisingTotal(Data: PAnsiChar; AWidth, AHeight: Integer;
  Model: TOperModel; const Area: TQuadrilateral; AValue: AnsiChar): Integer;

procedure Swell(SourceData, TargetData: PAnsiChar; AWidth, AHeight: Integer;
  Model: TOperModel; const Area: TQuadrilateral); overload;

procedure Swell(SourceData, TargetData: PAnsiChar; AWidth, AHeight: Integer;
  Model: TOperModel); overload;

function TotalValueCount(Data: PAnsiChar; AWidth, AHeight: Integer;
  const Area: TQuadrilateral; AValue: AnsiChar): Integer;

function CreateLine(ABegin, AEnd: TPoint): TLine; overload;
function CreateLine(ABeginX, ABeginY, AEndX, AEndY: Integer): TLine; overload;

function RGB2TColor(const R, G, B: Byte): Integer;
procedure TColor2RGB(const Color: TColor; var R, G, B: Byte);

procedure UnionPic(PicPaths: array of String; SavePath: String;
  Quality: Integer);

implementation

uses GdiPlus;

procedure UnionPic(PicPaths: array of String; SavePath: String;
  Quality: Integer);
var
  MainPic: IGPImage;
  DrawPic: TGPImage;
  OutPic: IGpBitmap;
  gp: IGPGraphics;
  SubImgHeight, SubImgWidth: Integer;
  i, X, BeginIndex: Integer;
  Parameters: IGPEncoderParameters;
begin
  MainPic := TGPImage.Create(PicPaths[0]);
  SubImgHeight := round(MainPic.Height / 3);
  SubImgWidth := Trunc((MainPic.Width / MainPic.Height) * SubImgHeight);

  OutPic := TGpBitmap.Create(MainPic.Width, MainPic.Height + SubImgHeight,
    PixelFormat32bppARGB);
  gp := TGpGraphics.Create(OutPic);
  gp.DrawImage(MainPic, 0, 0, MainPic.Width, MainPic.Height);
  X := 0;
  BeginIndex := Low(PicPaths);
  if High(PicPaths) > 2 then
      BeginIndex := BeginIndex + 1;
  for i := BeginIndex to High(PicPaths) do
  begin
    DrawPic := TGPImage.Create(PicPaths[i]);
    try
      gp.DrawImage(DrawPic, X, MainPic.Height, SubImgWidth, SubImgHeight);
    finally
      DrawPic.Free;
    end;
    X := X + SubImgWidth;
    if X >= MainPic.Width then
      Break;
  end;
  Parameters := TGPEncoderParameters.Create();
  Parameters.Add(EncoderQuality, 1, EncoderParameterValueTypeLong, @Quality);
  OutPic.Save(SavePath, TGPImageFormat.Jpeg, Parameters);
end;

function RGB2TColor(const R, G, B: Byte): Integer;
begin
  // convert hexa-decimal values to RGB
  Result := R + G shl 8 + B shl 16;
end;


procedure TColor2RGB(const Color: TColor; var R, G, B: Byte);
begin
  R := Color and $FF;
  G := (Color shr 8) and $FF;
  B := (Color shr 16) and $FF;
end;

function CreateLine(ABegin, AEnd: TPoint): TLine;
begin
  Result.BeginPoint := ABegin;
  Result.EndPoint := AEnd;
end;

function CreateLine(ABeginX, ABeginY, AEndX, AEndY: Integer): TLine;
begin
  Result.BeginPoint.X := ABeginX;
  Result.EndPoint.X := AEndX;
  Result.BeginPoint.Y := ABeginY;
  Result.EndPoint.Y := AEndY;
end;

function CoordinateConvert(SourceWidth, SourceHeight: Integer;
  const X, Y: Integer; TaregtWidht, TargetHeight: Integer): TPoint;
var
  WidthScale, HeightScale: real;
begin
  WidthScale := TaregtWidht / SourceWidth;
  HeightScale := TargetHeight / SourceHeight;
  Result.X := round(X * WidthScale);
  Result.Y := round(Y * HeightScale);
end;

function CoordinateConvert(SourceWidth, SourceHeight: Integer;
  const Coordinate: TPoint; TaregtWidht, TargetHeight: Integer): TPoint;
var
  WidthScale, HeightScale: real;
begin
  WidthScale := TaregtWidht / SourceWidth;
  HeightScale := TargetHeight / SourceHeight;
  Result.X := round(Coordinate.X * WidthScale);
  Result.Y := round(Coordinate.Y * HeightScale);
end;

function TotalValueCount(Data: PAnsiChar; AWidth, AHeight: Integer;
  const Area: TQuadrilateral; AValue: AnsiChar): Integer;

var
  TopYCount: Integer;
  TopStep, LeftStep, RightStep: real;
  TopLineX, LeftLineX, RightLineX: real;
  LeftYCount: Integer;
  RightYCount: Integer;
  Y, X: Integer;
  ValueCount: Integer;

  procedure TargetOper(X, Y: Integer);
  var
    Index: Integer;
  begin
    Index := AHeight * AWidth * 3 - Y * AWidth * 3 + X * 3;
    if (Index < AHeight * AWidth * 3) and (Data[Index] = AValue) and
      (Data[Index + 1] = AValue) and (Data[Index + 2] = AValue) then
    begin
      ValueCount := ValueCount + 1;
    end;
  end;

begin
  ValueCount := 0;

  LeftYCount := ABS(Area.LeftBottom.Y - Area.LeftTop.Y);
  LeftStep := (Area.LeftBottom.X - Area.LeftTop.X) / LeftYCount;

  RightYCount := ABS(Area.RightBottom.Y - Area.RightTop.Y);
  RightStep := (Area.RightBottom.X - Area.RightTop.X) / RightYCount;

  TopYCount := ABS(Area.LeftTop.Y - Area.RightTop.Y);

  LeftLineX := Area.LeftTop.X;
  RightLineX := Area.RightTop.X;
  if TopYCount > 0 then
  begin
    TopStep := ABS(Area.RightTop.X - Area.LeftTop.X) / TopYCount;
    if Area.LeftTop.Y < Area.RightTop.Y then
    begin

      TopLineX := Area.LeftTop.X;
      for Y := Area.LeftTop.Y to Area.RightTop.Y - 1 do
      begin
        for X := round(LeftLineX) to round(TopLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX + TopStep;
        LeftLineX := LeftLineX + LeftStep;
      end;
    end
    else
    begin
      TopLineX := Area.RightTop.X;

      for Y := Area.RightTop.Y to Area.LeftTop.Y - 1 do
      begin
        for X := round(TopLineX) to round(RightLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX - TopStep;
        RightLineX := RightLineX + RightStep;
      end;
    end;
  end;

  for Y := Max(Area.RightTop.Y, Area.LeftTop.Y) to Min(Area.LeftBottom.Y,
    Area.RightBottom.Y) do
  begin
    for X := round(LeftLineX) to round(RightLineX) do
    begin
      TargetOper(X, Y);
    end;
    RightLineX := RightLineX + RightStep;
    LeftLineX := LeftLineX + LeftStep;
  end;

  // -------------Bottom
  TopYCount := ABS(Area.LeftBottom.Y - Area.RightBottom.Y);
  if TopYCount > 0 then
  begin
    TopStep := ABS(Area.LeftBottom.X - Area.RightBottom.X) / TopYCount;
    if Area.LeftBottom.Y < Area.RightBottom.Y then
    begin
      TopLineX := Area.LeftBottom.X;
      for Y := Area.LeftBottom.Y + 1 to Area.RightBottom.Y do
      begin

        for X := round(TopLineX) to round(RightLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX + TopStep;
        RightLineX := RightLineX + RightStep;
      end;
    end
    else
    begin
      TopLineX := Area.RightBottom.X;
      for Y := Area.RightBottom.Y + 1 to Area.LeftBottom.Y do
      begin

        for X := round(LeftLineX) to round(TopLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX - TopStep;
        LeftLineX := LeftLineX + LeftStep;
      end;
    end;
  end;
  Result := ValueCount;
end;

procedure DiffValue(SourceData, TargetData: PAnsiChar; AWidth, AHeight: Integer;
  const Area: TQuadrilateral; Threshold: Integer);

  procedure TargetOper(X, Y: Integer);
  var
    Index, gray: Integer;
  begin
    Index := AHeight * AWidth * 3 - Y * AWidth * 3 + X * 3;
    if Index < AHeight * AWidth * 3 then
    begin
      TargetData[Index] :=
        AnsiChar(ABS(byte(SourceData[Index]) - byte(TargetData[Index])));
      TargetData[Index + 1] :=
        AnsiChar(ABS(byte(SourceData[Index + 1]) -
        byte(TargetData[Index + 1])));
      TargetData[Index + 2] :=
        AnsiChar(ABS(byte(SourceData[Index + 2]) -
        byte(TargetData[Index + 2])));
      if Threshold >= 0 then
      begin
        gray := round(byte(TargetData[Index + 2]) * 0.3 +
          byte(TargetData[Index + 1]) * 0.59 + byte(TargetData[Index]) * 0.11);
        if gray > Threshold then
        begin
          TargetData[Index] := AnsiChar(255);
          TargetData[Index + 1] := AnsiChar(255);
          TargetData[Index + 2] := AnsiChar(255);
        end
        else
        begin
          TargetData[Index] := AnsiChar(0);
          TargetData[Index + 1] := AnsiChar(0);
          TargetData[Index + 2] := AnsiChar(0);
        end;
      end;

    end;
  end;

var
  TopYCount: Integer;
  TopStep, LeftStep, RightStep: real;
  TopLineX, LeftLineX, RightLineX: real;
  LeftYCount: Integer;
  RightYCount: Integer;
  Y, X: Integer;
begin
  LeftYCount := ABS(Area.LeftBottom.Y - Area.LeftTop.Y);
  LeftStep := (Area.LeftBottom.X - Area.LeftTop.X) / LeftYCount;

  RightYCount := ABS(Area.RightBottom.Y - Area.RightTop.Y);
  RightStep := (Area.RightBottom.X - Area.RightTop.X) / RightYCount;

  TopYCount := ABS(Area.LeftTop.Y - Area.RightTop.Y);

  LeftLineX := Area.LeftTop.X;
  RightLineX := Area.RightTop.X;
  if TopYCount > 0 then
  begin
    TopStep := ABS(Area.RightTop.X - Area.LeftTop.X) / TopYCount;
    if Area.LeftTop.Y < Area.RightTop.Y then
    begin

      TopLineX := Area.LeftTop.X;
      for Y := Area.LeftTop.Y to Area.RightTop.Y - 1 do
      begin
        for X := round(LeftLineX) to round(TopLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX + TopStep;
        LeftLineX := LeftLineX + LeftStep;
      end;
    end
    else
    begin
      TopLineX := Area.RightTop.X;

      for Y := Area.RightTop.Y to Area.LeftTop.Y - 1 do
      begin
        for X := round(TopLineX) to round(RightLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX - TopStep;
        RightLineX := RightLineX + RightStep;
      end;
    end;
  end;

  for Y := Max(Area.RightTop.Y, Area.LeftTop.Y) to Min(Area.LeftBottom.Y,
    Area.RightBottom.Y) do
  begin
    for X := round(LeftLineX) to round(RightLineX) do
    begin
      TargetOper(X, Y);
    end;
    RightLineX := RightLineX + RightStep;
    LeftLineX := LeftLineX + LeftStep;
  end;

  // -------------Bottom
  TopYCount := ABS(Area.LeftBottom.Y - Area.RightBottom.Y);
  if TopYCount > 0 then
  begin
    TopStep := ABS(Area.LeftBottom.X - Area.RightBottom.X) / TopYCount;
    if Area.LeftBottom.Y < Area.RightBottom.Y then
    begin
      TopLineX := Area.LeftBottom.X;
      for Y := Area.LeftBottom.Y + 1 to Area.RightBottom.Y do
      begin

        for X := round(TopLineX) to round(RightLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX + TopStep;
        RightLineX := RightLineX + RightStep;
      end;
    end
    else
    begin
      TopLineX := Area.RightBottom.X;
      for Y := Area.RightBottom.Y + 1 to Area.LeftBottom.Y do
      begin

        for X := round(LeftLineX) to round(TopLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX - TopStep;
        LeftLineX := LeftLineX + LeftStep;
      end;
    end;
  end;
end;

procedure DiffValue(SourceData, TargetData: PAnsiChar;
  AWidth, AHeight: Integer);
var
  i: Integer;
begin
  for i := 0 to AWidth * AHeight * 3 - 1 do
  begin
    TargetData[i] := AnsiChar(ABS(byte(SourceData[i]) - byte(TargetData[i])));
  end;
end;

function CheckIntersection(Data: PAnsiChar; AWidth, AHeight: Integer;
  Model: TOperModel; X, Y: Integer; Value: AnsiChar): Boolean;
  function HorizontalCheck(PData: PAnsiChar; Step, CX: Integer): Boolean;
  begin
    Result := False;
    if ((CX + Step) < AWidth) and (PData[3 * (CX + Step)] = Value) and
      (PData[3 * (CX + Step) + 1] = Value) and
      (PData[3 * (CX + Step) + 2] = Value) then
    begin
      Result := true;
      Exit;
    end;

    if ((CX - Step) >= 0) and (PData[3 * (CX - Step)] = Value) and
      (PData[3 * (CX - Step) + 1] = Value) and
      (PData[3 * (CX - Step) + 2] = Value) then
    begin
      Result := true;
      Exit;
    end;
  end;

var
  p: PAnsiChar;
  i: Integer;
begin
  Result := False;

  for i := 1 to Model.Size do
  begin
    if (Model.Templete = doVertical) or (Model.Templete = doCross) or
      (Model.Templete = doRect) then
    begin
      if (Y - i) >= 0 then
      begin
        p := Data + (Y - i) * AWidth * 3;
        if (p[3 * X] = Value) and (p[3 * X + 1] = Value) and
          (p[3 * X + 2] = Value) then
        begin
          Result := true;
          Exit;
        end;
        if Model.Templete = doRect then
        begin
          if HorizontalCheck(p, i, X) then
          begin
            Result := true;
            Exit;
          end;
        end;
      end;

      if ((Y + i) < AHeight) then
      begin
        p := Data + (Y + i) * AWidth * 3;
        if (p[3 * X] = Value) and (p[3 * X + 1] = Value) and
          (p[3 * X + 2] = Value) then
        begin
          Result := true;
          Exit;
        end;
        if Model.Templete = doRect then
        begin
          if HorizontalCheck(p, i, X) then
          begin
            Result := true;
            Exit;
          end;
        end;
      end;
    end;

    if Model.Templete <> doVertical then
    begin
      p := Data + Y * AWidth * 3;
      if HorizontalCheck(p, i, X) then
      begin
        Result := true;
        Exit;
      end;
    end;
  end;
end;

procedure SwellOrErode(SourceData, TargetData: PAnsiChar;
  AWidth, AHeight: Integer; Model: TOperModel; fromValue, toValue: AnsiChar);
var
  X, Y: Integer;
  o: PAnsiChar;
begin
  CopyMemory(TargetData, SourceData, AWidth * AHeight * 3);
  for Y := 1 to AHeight - 2 do
  begin
    o := TargetData + Y * AWidth * 3;
    for X := 1 to AWidth - 2 do
    begin

      if ((o[3 * X] = fromValue) and (o[3 * X + 1] = fromValue) and
        (o[3 * X + 2] = fromValue)) then
      begin
        if CheckIntersection(SourceData, AWidth, AHeight, Model, X, Y,
          toValue) then
        begin
          o[3 * X] := toValue;
          o[3 * X + 1] := toValue;
          o[3 * X + 2] := toValue;
        end;
      end;
    end;
  end;
end;

procedure Swell(SourceData, TargetData: PAnsiChar; AWidth, AHeight: Integer;
  Model: TOperModel);
begin
  SwellOrErode(SourceData, TargetData, AWidth, AHeight, Model, AnsiChar(255),
    AnsiChar(0));
end;

procedure Erode(SourceData, TargetData: PAnsiChar; AWidth, AHeight: Integer;
  Model: TOperModel);
begin
  SwellOrErode(SourceData, TargetData, AWidth, AHeight, Model, AnsiChar(0),
    AnsiChar(255));
end;

function DenoisingTotal(Data: PAnsiChar; AWidth, AHeight: Integer;
  Model: TOperModel; const Area: TQuadrilateral; AValue: AnsiChar): Integer;
var
  TopYCount: Integer;
  TopStep, LeftStep, RightStep: real;
  TopLineX, LeftLineX, RightLineX: real;
  LeftYCount: Integer;
  RightYCount: Integer;
  Y, X: Integer;
  ValueCount: Integer;
  ToValue: AnsiChar;

  procedure TargetOper(X, Y: Integer);
  var
    o: PAnsiChar;
  begin
    o := Data + (AHeight - Y) * AWidth * 3;


    if (o[3 * X] = AValue) and (o[3 * X + 1] = AValue) and
      (o[3 * X + 2] = AValue) and not CheckIntersection(Data, AWidth, AHeight,
      Model, X, AHeight - Y, ToValue) then
      ValueCount := ValueCount + 1;
  end;

begin
  ValueCount := 0;
  if AValue = AnsiChar(0) then
    ToValue := AnsiChar(255)
  else
    ToValue := AnsiChar(0);

  LeftYCount := ABS(Area.LeftBottom.Y - Area.LeftTop.Y);
  LeftStep := (Area.LeftBottom.X - Area.LeftTop.X) / LeftYCount;

  RightYCount := ABS(Area.RightBottom.Y - Area.RightTop.Y);
  RightStep := (Area.RightBottom.X - Area.RightTop.X) / RightYCount;

  TopYCount := ABS(Area.LeftTop.Y - Area.RightTop.Y);

  LeftLineX := Area.LeftTop.X;
  RightLineX := Area.RightTop.X;
  if TopYCount > 0 then
  begin
    TopStep := ABS(Area.RightTop.X - Area.LeftTop.X) / TopYCount;
    if Area.LeftTop.Y < Area.RightTop.Y then
    begin

      TopLineX := Area.LeftTop.X;
      for Y := Area.LeftTop.Y to Area.RightTop.Y - 1 do
      begin
        for X := round(LeftLineX) to round(TopLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX + TopStep;
        LeftLineX := LeftLineX + LeftStep;
      end;
    end
    else
    begin
      TopLineX := Area.RightTop.X;

      for Y := Area.RightTop.Y to Area.LeftTop.Y - 1 do
      begin
        for X := round(TopLineX) to round(RightLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX - TopStep;
        RightLineX := RightLineX + RightStep;
      end;
    end;
  end;

  for Y := Max(Area.RightTop.Y, Area.LeftTop.Y) to Min(Area.LeftBottom.Y,
    Area.RightBottom.Y) do
  begin
    for X := round(LeftLineX) to round(RightLineX) do
    begin
      TargetOper(X, Y);
    end;
    RightLineX := RightLineX + RightStep;
    LeftLineX := LeftLineX + LeftStep;
  end;

  // -------------Bottom
  TopYCount := ABS(Area.LeftBottom.Y - Area.RightBottom.Y);
  if TopYCount > 0 then
  begin
    TopStep := ABS(Area.LeftBottom.X - Area.RightBottom.X) / TopYCount;
    if Area.LeftBottom.Y < Area.RightBottom.Y then
    begin
      TopLineX := Area.LeftBottom.X;
      for Y := Area.LeftBottom.Y + 1 to Area.RightBottom.Y do
      begin

        for X := round(TopLineX) to round(RightLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX + TopStep;
        RightLineX := RightLineX + RightStep;
      end;
    end
    else
    begin
      TopLineX := Area.RightBottom.X;
      for Y := Area.RightBottom.Y + 1 to Area.LeftBottom.Y do
      begin

        for X := round(LeftLineX) to round(TopLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX - TopStep;
        LeftLineX := LeftLineX + LeftStep;
      end;
    end;
  end;
  Result := ValueCount;
end;

procedure SwellOrErodeByArea(SourceData, TargetData: PAnsiChar;
  AWidth, AHeight: Integer; Model: TOperModel; const Area: TQuadrilateral;
  fromValue, toValue: AnsiChar);
var
  TopYCount: Integer;
  TopStep, LeftStep, RightStep: real;
  TopLineX, LeftLineX, RightLineX: real;
  LeftYCount: Integer;
  RightYCount: Integer;
  Y, X: Integer;

  procedure TargetOper(X, Y: Integer);
  var
    o: PAnsiChar;
  begin
    o := TargetData + (AHeight - Y) * AWidth * 3;

    if ((o[3 * X] = fromValue) and (o[3 * X + 1] = fromValue) and
      (o[3 * X + 2] = fromValue)) then
    begin
      if CheckIntersection(SourceData, AWidth, AHeight, Model, X, AHeight - Y,
        toValue) then
      begin
        o[3 * X] := toValue;
        o[3 * X + 1] := toValue;
        o[3 * X + 2] := toValue;
      end;
    end;
  end;

begin
  CopyMemory(TargetData, SourceData, AWidth * AHeight * 3);

  LeftYCount := ABS(Area.LeftBottom.Y - Area.LeftTop.Y);
  LeftStep := (Area.LeftBottom.X - Area.LeftTop.X) / LeftYCount;

  RightYCount := ABS(Area.RightBottom.Y - Area.RightTop.Y);
  RightStep := (Area.RightBottom.X - Area.RightTop.X) / RightYCount;

  TopYCount := ABS(Area.LeftTop.Y - Area.RightTop.Y);

  LeftLineX := Area.LeftTop.X;
  RightLineX := Area.RightTop.X;
  if TopYCount > 0 then
  begin
    TopStep := ABS(Area.RightTop.X - Area.LeftTop.X) / TopYCount;
    if Area.LeftTop.Y < Area.RightTop.Y then
    begin

      TopLineX := Area.LeftTop.X;
      for Y := Area.LeftTop.Y to Area.RightTop.Y - 1 do
      begin
        for X := round(LeftLineX) to round(TopLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX + TopStep;
        LeftLineX := LeftLineX + LeftStep;
      end;
    end
    else
    begin
      TopLineX := Area.RightTop.X;

      for Y := Area.RightTop.Y to Area.LeftTop.Y - 1 do
      begin
        for X := round(TopLineX) to round(RightLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX - TopStep;
        RightLineX := RightLineX + RightStep;
      end;
    end;
  end;

  for Y := Max(Area.RightTop.Y, Area.LeftTop.Y) to Min(Area.LeftBottom.Y,
    Area.RightBottom.Y) do
  begin
    for X := round(LeftLineX) to round(RightLineX) do
    begin
      TargetOper(X, Y);
    end;
    RightLineX := RightLineX + RightStep;
    LeftLineX := LeftLineX + LeftStep;
  end;

  // -------------Bottom
  TopYCount := ABS(Area.LeftBottom.Y - Area.RightBottom.Y);
  if TopYCount > 0 then
  begin
    TopStep := ABS(Area.LeftBottom.X - Area.RightBottom.X) / TopYCount;
    if Area.LeftBottom.Y < Area.RightBottom.Y then
    begin
      TopLineX := Area.LeftBottom.X;
      for Y := Area.LeftBottom.Y + 1 to Area.RightBottom.Y do
      begin

        for X := round(TopLineX) to round(RightLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX + TopStep;
        RightLineX := RightLineX + RightStep;
      end;
    end
    else
    begin
      TopLineX := Area.RightBottom.X;
      for Y := Area.RightBottom.Y + 1 to Area.LeftBottom.Y do
      begin

        for X := round(LeftLineX) to round(TopLineX) do
        begin
          TargetOper(X, Y);
        end;
        TopLineX := TopLineX - TopStep;
        LeftLineX := LeftLineX + LeftStep;
      end;
    end;
  end;
end;

procedure Swell(SourceData, TargetData: PAnsiChar; AWidth, AHeight: Integer;
  Model: TOperModel; const Area: TQuadrilateral);
begin
  SwellOrErodeByArea(SourceData, TargetData, AWidth, AHeight, Model, Area,
    AnsiChar(255), AnsiChar(0));
end;

procedure Erode(SourceData, TargetData: PAnsiChar; AWidth, AHeight: Integer;
  Model: TOperModel; const Area: TQuadrilateral);
begin
  SwellOrErodeByArea(SourceData, TargetData, AWidth, AHeight, Model, Area,
    AnsiChar(0), AnsiChar(255));
end;

end.
